import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.ImageIcon;

import clientAPI.*;

/**
 * The ClientListener class opens a connection to a socket, and awaits
 * connection from a client. The communication must be in the form specified in
 * the clientAPI package.
 * 
 * @author jonathan
 */
public class ClientListener extends Thread {
	Socket connection = null;
	ServerSocket providerSocket;
	ObjectOutputStream out;
	ObjectInputStream in;
	ClientMessage cmessage;
	ServerMessage smessage;
	ThreadCommunication comm;
	VideoStreamServer videoStreamServer;

	/**
	 * The constructor sets up a connection to a socket.
	 * 
	 * @param socket
	 *            The socket number. This should be 46368 in general.
	 */
	public ClientListener(ThreadCommunication comm, int socket) {
		this.comm = comm;
		try {
			providerSocket = new ServerSocket(socket);
		} catch (IOException ioexp) {
			System.out.println("Could not create providerSocket: " + ioexp);
		}
	}

	/**
	 * This function is used by starting a new thread with the start() command.
	 * Most of the work is done here. This function will listen for a client,
	 * then continuously receive messages from the client until the connection
	 * is terminated. It will then wait for a new client to connect.
	 */
	public void run() {
		while (true) {
			try {
				// 46368 = 24th fibonacci. 75025 = 25th fibonacci.
				System.out.println("Waiting for connection");
				connection = providerSocket.accept();
				System.out.println("Connection received from "
						+ connection.getInetAddress().getHostName() + " on port: " + connection.getLocalPort());

				out = new ObjectOutputStream(connection.getOutputStream());
				in = new ObjectInputStream(connection.getInputStream());
				out.flush();

				BTReceiver rec = new BTReceiver();
				BTSender send = new BTSender();
				videoStreamServer = new VideoStreamServer(46370);
				
				rec.start();
				send.start();
				videoStreamServer.start();
				
				while(true) {
					if (rec.getState() == Thread.State.TERMINATED ||
						send.getState() == Thread.State.TERMINATED	){
						in.close();
						out.close();
						break;
					} else {
						Thread.sleep(200);
					}
				}

				// providerSocket.close();
			} catch (IOException ioException) {
				ioException.printStackTrace();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("end of loop.");
		}
	}

	/**
	 * This is a help function to send a message to a client.
	 * 
	 * @param msg
	 *            The message to be sent is of the type ServerMessage, found in
	 *            the clientAPI package.
	 * @throws IOException
	 */
	void sendMessage(ServerMessage msg) throws IOException {
		try {
			out.writeObject(msg);
			out.flush();
			System.out.println("server>" + msg);
		} catch (IOException e) {
			throw e;
		}
	}
	
	/**
	 * Sends images from the camera to the client.
	 * @param image Images from the camera is sent as ImageIcons.
	 * @throws IOException
	 */
	void sendCameraMessage(ImageIcon msg) throws IOException {
		try {
			out.writeObject(msg);
			out.flush();
		} catch (IOException e) {
			throw e;
		}
	}
	

	class BTReceiver extends Thread {
		BTReceiver() {
		}

		public void run() {
			ClientMessage cmessage = null;
			do {
				try {
					cmessage = (ClientMessage) in.readObject();
					comm.addClientMessage(cmessage);
				} catch (IOException e) {
					System.out.println("IOException: " + e);
					break;
				} catch (ClassNotFoundException e) {
					System.out.println("Class not found: " + e);
				}
			} while (!cmessage.willTerminate());
		}
	}

	class BTSender extends Thread {
		BTSender() {
		}

		public void run() {
			ServerMessage smessage = null;
			try {
				do {
					while (smessage == null)
						smessage = comm.getServerMessage();
					sendMessage(smessage);
					smessage = null;
				} while (true);
			} catch (IOException e) {
				System.out.println("IOException " + e);
			}
		}
	}
}
